package com.gitee.wsl.compose.ui.richtext.edit.state

import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.text.TextRange
import com.gitee.wsl.compose.ui.richtext.edit.annotation.ExperimentalRichTextApi
import com.gitee.wsl.compose.ui.richtext.edit.model.span.RichSpan
import com.gitee.wsl.compose.ui.richtext.edit.model.span.style.Link
import com.gitee.wsl.compose.ui.richtext.edit.model.span.style.RichSpanStyle

/**
 * Returns whether the current selected text is a link.
 */
@OptIn(ExperimentalRichTextApi::class)
val RichTextState.isLink: Boolean get() = currentRichSpanStyle::class == Link::class

/**
 * Returns the selected link text.
 */
val RichTextState.selectedLinkText: String? get() =
    if (isLink)
        getRichSpanByTextIndex(textIndex = selection.min - 1)?.text
    else
        null

/**
 * Returns the selected link URL.
 */
@OptIn(ExperimentalRichTextApi::class)
val RichTextState.selectedLinkUrl: String? get() = (currentRichSpanStyle as? Link)?.url

@OptIn(ExperimentalRichTextApi::class)
internal fun RichTextState.getLinkByOffset(offset: Offset): String? {
    val richSpan = getRichSpanByOffset(offset)
    val linkRichSpan = getLinkRichSpan(richSpan)

    return (linkRichSpan?.richSpanStyle as? Link)?.url
}

internal fun RichTextState.isLink(offset: Offset): Boolean {
    val richSpan = getRichSpanByOffset(offset)
    val linkRichSpan = getLinkRichSpan(richSpan)

    return linkRichSpan != null
}

/**
 * Add a link to the text field.
 * The link is going to be added after the current selection.
 *
 * @param text the text of the link.
 * @param url the URL of the link.
 */
@OptIn(ExperimentalRichTextApi::class)
fun RichTextState.addLink(
    text: String,
    url: String,
) {
    if (text.isEmpty()) return

    val paragraph = richParagraphList.firstOrNull() ?: return
    val linkStyle = Link(url = url)
    val linkRichSpan = RichSpan(
        text = text,
        richSpanStyle = linkStyle,
        paragraph = paragraph,
    )

    addRichSpanAtPosition(
        linkRichSpan,
        index = selection.min,
    )

    val beforeText = textFieldValue.text.substring(0, selection.min)
    val afterText = textFieldValue.text.substring(selection.min)
    val newText = "$beforeText$text$afterText"
    updateTextFieldValue(
        newTextFieldValue = textFieldValue.copy(
            text = newText,
            selection = TextRange(selection.min + text.length),
        )
    )
}


/**
 * Add a link to the selected text.
 *
 * @param url the URL of the link.
 */
@OptIn(ExperimentalRichTextApi::class)
fun RichTextState.addLinkToSelection(
    url: String,
) {
    if (selection.collapsed) return

    val linkStyle = Link(url = url)

    toAddRichSpanStyle = linkStyle
    toRemoveRichSpanStyle = RichSpanStyle.Default::class

    addRichSpan(
        spanStyle = linkStyle
    )
}

/**
 * Update the link of the selected text.
 *
 * @param url the new URL of the link.
 */
@OptIn(ExperimentalRichTextApi::class)
fun RichTextState.updateLink(
    url: String,
) {
    if (!isLink) return

    val linkStyle = Link(
        url = url,
    )

    val richSpan = getSelectedLinkRichSpan() ?: return

    richSpan.richSpanStyle = linkStyle

    updateTextFieldValue(textFieldValue)
}

/**
 * Remove the link from the selected text.
 */
@OptIn(ExperimentalRichTextApi::class)
fun RichTextState.removeLink() {
    if (!isLink) return

    val richSpan = getSelectedLinkRichSpan() ?: return

    richSpan.richSpanStyle = RichSpanStyle.Default

    updateTextFieldValue(textFieldValue)
}

internal fun RichTextState.getSelectedLinkRichSpan(): RichSpan? {
    val richSpan = getRichSpanByTextIndex(selection.min - 1)

    return getLinkRichSpan(richSpan)
}


@OptIn(ExperimentalRichTextApi::class)
internal fun RichTextState.getLinkRichSpan(initialRichSpan: RichSpan?): RichSpan? {
    var richSpan = initialRichSpan

    while (richSpan != null && richSpan.richSpanStyle !is Link) {
        richSpan = richSpan.parent
    }

    return richSpan
}